<!-- 
  @author: 善良的YWJ
  @Created by shanliangdeYWJ on 2021/07/07 02:43:40
  @file: Composition.vue
  @description:
  ```
  抽离 Composition 到这个文件
  ```
  @link [Github](https://github.com/shanliangdeYWJ)
-->

<template>
  <div class="Composition">
    <p>counter: {{ counter }}</p>
    <p>{{ doubleCounter }}</p>
    <p>{{ msg2 }}</p>
    <p ref="desc"></p>
  </div>
</template>

<script>
import { reactive, ref, toRefs } from "@vue/reactivity";
import { computed, onMounted, onUnmounted, watch } from "@vue/runtime-core";
export default {
  name: "Composition",
  setup() {
    const { counter, doubleCounter } = useCounter();

    const msg2 = ref("some message");

    // 使用元素的引用 [ref](https://v3.cn.vuejs.org/guide/composition-api-template-refs.html#模板引用)
    // 在使用组合式 API 时，响应式引用和模板引用的概念是统一的。
    // 为了获得对模板内元素或组件实例的引用，我们可以像往常一样声明 ref 并从 setup() 返回
    const desc = ref(null);

    watch(counter, (val, oldVal) => {
      const p = desc.value;
      p.textContent = `counter change from ${oldVal} to ${val}`;
    });

    return { counter, doubleCounter, msg2, desc };
  },
};

// 抽取 setup 函数内部代码，组织代码逻辑位置
function useCounter() {
  // reactive 包裹响应式【对象】数据
  const data = reactive({
    counter: 1,
    doubleCounter: computed(() => data.counter * 2),
  });

  let timer;

  onMounted(() => {
    // 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的，所以不需要显式地定义
    // 它们。换句话说，在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
    // setup 内部没有时间函数，所以创造了只能在 setup 内部才能使用的钩子生命周期
    // https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html#生命周期钩子
    timer = setTimeout(() => {
      data.counter++;
    }, 1000);
  });

  onUnmounted(() => {
    // 已经卸载
    clearInterval(timer);
  });
  // 引用都是 data. 开头会冗余，所以用toRefs 包裹调用时候可以展开，
  // 此时页面上用data.counter，显示的就是对象而不是值了。
  return toRefs(data);
}
</script>

<style></style>
